// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_HANDLER_TABLE_H_
#define V8_HANDLER_TABLE_H_

#include "src/assert-scope.h"
#include "src/globals.h"
#include "src/utils.h"

namespace v8 {
namespace internal {

    class Assembler;
    class ByteArray;
    class BytecodeArray;

    // HandlerTable is a byte array containing entries for exception handlers in
    // the code object it is associated with. The tables come in two flavors:
    // 1) Based on ranges: Used for unoptimized code. Stored in a {ByteArray} that
    //    is attached to each {BytecodeArray}. Contains one entry per exception
    //    handler and a range representing the try-block covered by that handler.
    //    Layout looks as follows:
    //      [ range-start , range-end , handler-offset , handler-data ]
    // 2) Based on return addresses: Used for turbofanned code. Stored directly in
    //    the instruction stream of the {Code} object. Contains one entry per
    //    call-site that could throw an exception. Layout looks as follows:
    //      [ return-address-offset , handler-offset ]
    class V8_EXPORT_PRIVATE HandlerTable {
    public:
        // Conservative prediction whether a given handler will locally catch an
        // exception or cause a re-throw to outside the code boundary. Since this is
        // undecidable it is merely an approximation (e.g. useful for debugger).
        enum CatchPrediction {
            UNCAUGHT, // The handler will (likely) rethrow the exception.
            CAUGHT, // The exception will be caught by the handler.
            PROMISE, // The exception will be caught and cause a promise rejection.
            DESUGARING, // The exception will be caught, but both the exception and
            // the catching are part of a desugaring and should therefore
            // not be visible to the user (we won't notify the debugger of
            // such exceptions).
            ASYNC_AWAIT, // The exception will be caught and cause a promise rejection
            // in the desugaring of an async function, so special
            // async/await handling in the debugger can take place.
        };

        // Constructors for the various encodings.
        explicit HandlerTable(Code code);
        explicit HandlerTable(ByteArray byte_array);
        explicit HandlerTable(BytecodeArray bytecode_array);
        explicit HandlerTable(Address instruction_start, size_t handler_table_offset);

        // Getters for handler table based on ranges.
        int GetRangeStart(int index) const;
        int GetRangeEnd(int index) const;
        int GetRangeHandler(int index) const;
        int GetRangeData(int index) const;

        // Setters for handler table based on ranges.
        void SetRangeStart(int index, int value);
        void SetRangeEnd(int index, int value);
        void SetRangeHandler(int index, int offset, CatchPrediction pred);
        void SetRangeData(int index, int value);

        // Returns the required length of the underlying byte array.
        static int LengthForRange(int entries);

        // Emitters for handler table based on return addresses.
        static int EmitReturnTableStart(Assembler* masm, int entries);
        static void EmitReturnEntry(Assembler* masm, int offset, int handler);

        // Lookup handler in a table based on ranges. The {pc_offset} is an offset to
        // the start of the potentially throwing instruction (using return addresses
        // for this value would be invalid).
        int LookupRange(int pc_offset, int* data, CatchPrediction* prediction);

        // Lookup handler in a table based on return addresses.
        int LookupReturn(int pc_offset);

        // Returns the number of entries in the table.
        int NumberOfRangeEntries() const;
        int NumberOfReturnEntries() const;

#ifdef ENABLE_DISASSEMBLER
        void HandlerTableRangePrint(std::ostream& os); // NOLINT
        void HandlerTableReturnPrint(std::ostream& os); // NOLINT
#endif

    private:
        enum EncodingMode { kRangeBasedEncoding,
            kReturnAddressBasedEncoding };

        // Getters for handler table based on ranges.
        CatchPrediction GetRangePrediction(int index) const;

        // Getters for handler table based on return addresses.
        int GetReturnOffset(int index) const;
        int GetReturnHandler(int index) const;

        // Number of entries in the loaded handler table.
        int number_of_entries_;

#ifdef DEBUG
        // The encoding mode of the table. Mostly useful for debugging to check that
        // used accessors and constructors fit together.
        EncodingMode mode_;
#endif

        // Direct pointer into the encoded data. This pointer points into object on
        // the GC heap (either {ByteArray} or {Code}) and hence would become stale
        // during a collection. Hence we disallow any allocation.
        Address raw_encoded_data_;
        DISALLOW_HEAP_ALLOCATION(no_gc_)

        // Layout description for handler table based on ranges.
        static const int kRangeStartIndex = 0;
        static const int kRangeEndIndex = 1;
        static const int kRangeHandlerIndex = 2;
        static const int kRangeDataIndex = 3;
        static const int kRangeEntrySize = 4;

        // Layout description for handler table based on return addresses.
        static const int kReturnOffsetIndex = 0;
        static const int kReturnHandlerIndex = 1;
        static const int kReturnEntrySize = 2;

        // Encoding of the {handler} field.
        class HandlerPredictionField : public BitField<CatchPrediction, 0, 3> {
        };
        class HandlerOffsetField : public BitField<int, 3, 29> {
        };
    };

} // namespace internal
} // namespace v8

#endif // V8_HANDLER_TABLE_H_
